/*
Marcus Ruether
4-20-08
Desc: runs virtual machine
*/

#include<stdio.h>
#include<string.h>
#include"lexar.h"

int main(int argc, char *argv[])
{
	//take sourceCode as command line arg

	FILE *SOURCE;
	if(!(SOURCE = fopen(argv[1], "r")))
	{
		fprintf(stderr, "Source file specified on command line does not exit\nExiting Virtual Machine");
		exit(1);
	}
	
	char line[500];
	int numbOfOps;
	numbOfOps = 0;
	while(fgets(line, 500, SOURCE))
	{
		++numbOfOps;
	}

//	fprintf(stderr,"Number if Operations: %d\n", numbOfOps);
	
	struct tokenList **Operations = malloc(sizeof(struct tokenList *)*(numbOfOps));
	fclose(SOURCE);
	SOURCE = fopen(argv[1], "r");

	//tokenize inputs
	lex_main(SOURCE, stdout, stderr, Operations, numbOfOps);
	//operations are now stored in Operations[] as tokens
	//can close SOURCE file
	fclose(SOURCE);

	//we now have all operations in an array of tokenList
	struct vm//stores current oparations number(IP) and all register values
	{
		int reg[8]; 
		int IP;
	};
	struct vm vmState;
	//initial values of vm
	int k = 0;
	//initializes register of vmState
	for(k; k < 8;++k)
	{
		vmState.reg[k] = 0;
	}
	vmState.IP = 0;

//crap for debugging
/*
	k = 0;
	struct token *testTok;
	for(k; k < numbOfOps; k++)
	{
		fprintf(stderr,"OPERATION %d ------------------------------------\n", k+1);
		testTok = Operations[k]->head;
		while(testTok != NULL)
		{
			fprintf(stderr,"%s\n",&testTok->strVal);
			testTok = testTok->next;
		}
	}
*/
	k=0;
	while(vmState.IP < (numbOfOps))
	{
		++k;
		struct token *cmdToken;
		//up to four arguments, I dont think we will need more than that
		struct token *arg1;
		struct token *arg2;
		struct token *arg3;

		cmdToken = Operations[vmState.IP]->head;
		//all the if/else switches to determine the operation to take
		
		if(cmdToken == NULL)
		{
		}
		else if(cmdToken->type == JUNK)
		{
			fprintf(stderr,"Invalid Operation at line number: %d\n", cmdToken->lineNumb);
		}
		else if(cmdToken->type == COMMENT)
		{
		}
		else//at this point we can assume this first command token is a valid operation
		{
			//if cmdToken->head is MOV and the next token is not null
			if( (cmdToken->v.op == MOV) && (cmdToken->next != NULL) )
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;
				//if second token is is register and next token is not null
				if( (cmdToken->type == REGI) && (cmdToken->next != NULL))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					//if last token is register
					if(cmdToken->type == REGI)
					{	//args are vm, second token in list, and third token in list
						vmState.reg[arg1->v.r] = vmState.reg[arg2->v.r];
					//	fprintf(stdout,"MOVING REG INTO REG\n");
					}
					//if last token is int
					else if(cmdToken->type == INTEGER)
					{
						vmState.reg[arg1->v.r] = arg2->v.i;
					//	fprintf(stdout,"MOVING INT INTO REGI\n");
					}
					else
					{
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid aregument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

//Both Addition and Subtraction functions written by Kriston Michael Singh
			// Adds two reg's or a reg and an int and places the value into the first register
			//if cmdToken->head is ADD and the next token is not null
			else if( (cmdToken->v.op == ADD) && (cmdToken->next != NULL) )
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;
				//if second token is is register and next token is not null
				if( (cmdToken->type == REGI) && (cmdToken->next != NULL))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					//if last token is register
					if(cmdToken->type == REGI)
					{	//args are vm, second token in list, and third token in list
						vmState.reg[arg1->v.r] += vmState.reg[arg2->v.r];
					//	fprintf(stdout,"ADDING REG TO REG\n");
					}
					//if last token is int
					else if(cmdToken->type == INTEGER)
					{
						vmState.reg[arg1->v.r] += arg2->v.i;
					//	fprintf(stdout,"ADDING REG TO INT\n");
					}
					else
					{
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid aregument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}
			//Subtracts two reg's or a reg and an int and place the value in the first register
			//if cmdToken->head is SUB and the next token is not null
			else if( (cmdToken->type == OPCODE) && (cmdToken->v.op == SUB) && (cmdToken->next != NULL) )
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;
				//if second token is is register and next token is not null
				if( (cmdToken->type == REGI) && (cmdToken->next != NULL))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					//if last token is register
					if(cmdToken->type == REGI)
					{	//args are vm, second token in list, and third token in list
						vmState.reg[arg1->v.r] -= vmState.reg[arg2->v.r];
					//	fprintf(stdout,"SUBTRACTING REG FROM REG\n");
					}
					//if last token is int
					else if(cmdToken->type == INTEGER)
					{
						vmState.reg[arg1->v.r] -= arg2->v.i;
					//	fprintf(stdout,"SUBTRACTING REG FROM INT\n");
					}
					else
					{
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid aregument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

			//Both Multiply and Divide functions written by Jordan Allen-Baxter
			// Multiplies two reg's or a reg and an int and places the value into the first register
			//if cmdToken->head is MUL and the next token is not null
			else if( (cmdToken->v.op == MULT) && (cmdToken->next != NULL) )
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;
				//if second token is is register and next token is not null
				if( (cmdToken->type == REGI) && (cmdToken->next != NULL))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					//if last token is register
					if(cmdToken->type == REGI)
					{	//args are vm, second token in list, and third token in list
						vmState.reg[arg1->v.r] *= vmState.reg[arg2->v.r];
					//	fprintf(stdout,"MULTIPLYING REG BY REG\n");
					}
					//if last token is int
					else if(cmdToken->type == INTEGER)
					{
						vmState.reg[arg1->v.r] *= arg2->v.i;
					//	fprintf(stdout,"MULTIPLYING REG BY INT\n");
					}
					else
					{
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid aregument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}
			//Divides two reg's or a reg and an int and place the value in the first register
			//if cmdToken->head is DIV and the next token is not null
			else if( (cmdToken->type == OPCODE) && (cmdToken->v.op == DIV) && (cmdToken->next != NULL) )
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;
				//if second token is is register and next token is not null
				if( (cmdToken->type == REGI) && (cmdToken->next != NULL))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					//if last token is register
					if(cmdToken->type == REGI)
					{	//args are vm, second token in list, and third token in list
						vmState.reg[arg1->v.r] /= vmState.reg[arg2->v.r];
					//	fprintf(stdout,"DIVIDING REG BY REG\n");
					}
					//if last token is int
					else if(cmdToken->type == INTEGER)
					{
						vmState.reg[arg1->v.r] /= arg2->v.i;
					//	fprintf(stdout,"DIVIDING REG BY INT\n");
					}
					else
					{
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid aregument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

			//JGT, JLT, GET by  Ricky "Big Pimping" Biggers
			else if(cmdToken->v.op == JGT)
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;

				//checking for correct arguements; JGT(REGI,REGI,INTERGER);
				if((cmdToken->type == REGI) && (cmdToken->next->type == REGI) && (cmdToken->next->next->type == INTEGER))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					cmdToken = cmdToken->next;
					arg3 = cmdToken;

					//if REGI1 < REGI2 jump to line i
					if(vmState.reg[arg1->v.r] > vmState.reg[arg2->v.r])
					{
						vmState.IP += arg3->v.i -1;
					//	fprintf(stdout,"JGT WOOT!\n");
					}
				}
			}

			else if(cmdToken->v.op == JLT)
			{
				cmdToken = cmdToken->next;
				arg1 = cmdToken;

				//checking for correct arguments; JLT(REGI,REGI,INTERGER);
				if((cmdToken->type == REGI) && (cmdToken->next->type == REGI) && (cmdToken->next->next->type == INTEGER))
				{
					cmdToken = cmdToken->next;
					arg2 = cmdToken;
					cmdToken = cmdToken->next;
					arg3 = cmdToken;

					//if REGI1 < REGI2 jump to line i
					if(vmState.reg[arg1->v.r] < vmState.reg[arg2->v.r])
					{
						vmState.IP = arg3->v.i - 1;
					//	fprintf(stdout,"JLT WOOT!\n");
					}
				}
			}


			else if(cmdToken->v.op == GET)

			{
				int rickyisabitch;
				cmdToken = cmdToken->next;
				arg1 = cmdToken;

				//checking for correct arguments; PUT(REGI, INTEGER);
				if(cmdToken->type == REGI)
				{
					scanf("%d", &rickyisabitch);
					vmState.reg[arg1->v.i] = rickyisabitch;
				}
			}

			//JNZ, JNEQ, JEQ, BR -_- James Jennings
			//if cmdToken->head is JNZ and the next token is not null
			else if ((cmdToken->type == OPCODE) && (cmdToken->v.op == JNZ) && (cmdToken->next != NULL) ) {
				//if reg A != 0
				cmdToken = cmdToken->next;
				if (cmdToken->type == INTEGER) {
					if (vmState.reg[0] != 0) {
					//if 2nd cmd is an INTEGER then jump
						//has to be I-1 as IP will be incremented at end of while loop
						vmState.IP += (cmdToken->v.i-1);
					}
				}
				else
				{
					fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

			//if cmdToken->head is JNEQ and the next token is not null
			else if ( (cmdToken->type == OPCODE) && (cmdToken->v.op == JNEQ) && (cmdToken->next != NULL) ) {
				if ((cmdToken->next->type == REGI) && (cmdToken->next->next != NULL)) {
					if ((cmdToken->next->next->type == REGI) && (cmdToken->next->next->next != NULL)) {
						if ((vmState.reg[cmdToken->next->v.r] != vmState.reg[cmdToken->next->next->v.r]) && (cmdToken->next->next->next->type == INTEGER)) {
							vmState.IP += (cmdToken->next->next->next->v.i-1);
						}
						else {
						}
					}
					else {
						fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

			//if cmdToken->head is JEQ and the next token is not null
			else if ( (cmdToken->type == OPCODE) && (cmdToken->v.op == JEQ) && (cmdToken->next != NULL) ) {
				if ((cmdToken->next->type == REGI) && (cmdToken->next->next != NULL)) {
					if ((cmdToken->next->next->type == REGI) && (cmdToken->next->next->next != NULL)) {
						if ((vmState.reg[cmdToken->next->v.r] == vmState.reg[cmdToken->next->next->v.r]) && (cmdToken->next->next->next->type == INTEGER)) {
							vmState.IP += (cmdToken->next->next->next->v.i-1);
						}
						else {
						}
					}
					else {
						fprintf(stderr,"bInvalid argument(s) on line number: %d\n", cmdToken->lineNumb);
					}
				}
				else
				{
					fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
				}
			}

			//if cmdToken->head is BR and the next token is not null
			else if ( (cmdToken->type == OPCODE) && (cmdToken->v.op == BR)) {
				printf("\n");
			}

			//PUT, PUTC - Chris Jennings
                        //if cmdToken->head is PUT and the next token is not null
                        else if((cmdToken->type == OPCODE) && (cmdToken->v.op == PUT))
                        {
                                cmdToken = cmdToken->next;
                                arg1 = cmdToken;
				//if the type is valid, print it out
				if((cmdToken->type == REGI) && (cmdToken->next == NULL))
				{
                                        fprintf(stdout, "%d", vmState.reg[arg1->v.r]);
				}
                                else
                                {
                                	fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
                                }
			}
                        //if cmdToken->head is PUTC and the next token is not null
                        else if((cmdToken->type == OPCODE) && (cmdToken->v.op == PUTC))
                        {
                                cmdToken = cmdToken->next;
                                arg1 = cmdToken;
				//if the type is valid, print it's ASCII value
				if((cmdToken->type == REGI) && (cmdToken->next == NULL))
				{
				//??	arg1 = vmState.reg[arg1->v.i];
					if((arg1->v.i > 31) && (arg1->v.i < 127))
						putchar(arg1->v.i);
					else
                                        	fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
				}
                                else if((cmdToken->type == INTEGER) && (cmdToken->next == NULL))
                                {
                                        if((arg1->v.i > 31) && (arg1->v.i < 127))
                                                putchar(arg1->v.i );
                                        else
                                                fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
                                }
                                else
                                {
                                	fprintf(stderr,"Invalid argument(s) on line number: %d\n", cmdToken->lineNumb);
                                }
                        }
	
			else
			{			
				fprintf(stderr,"Invalid Operation on line number: %d\n", cmdToken->lineNumb);
			}		
			//repeat what I did with MOV for the rest of the operations...
			//this is going to be alot of repeatitive code!!!!!!!!!!!
		}
		vmState.IP++;
	}
	

	k=0;
	for(k; k < numbOfOps; k++)
	{
		delete_tokenList(Operations[k]);
		Operations[k] = NULL;
	}

	free (Operations);
}
